package com.rn.ddd.infrastructure.engine;

import com.rn.ddd.infrastructure.engine.annotation.OrderProcessor;
import com.rn.ddd.infrastructure.engine.constant.OrderEventEnum;
import com.rn.ddd.infrastructure.engine.constant.OrderStateEnum;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;

/**
 * 状态机注册器
 *
 * @author 然诺
 * @date 2022/3/5
 */
@Component
public class DefaultStateProcessorRegistry implements BeanPostProcessor {

    private static Map<String, Map<String, Map<String, List<AbstractStateProcessor>>>> stateProcessorMap = new ConcurrentHashMap<>();

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if (bean instanceof AbstractStateProcessor && bean.getClass().isAnnotationPresent(OrderProcessor.class)) {
            OrderProcessor annotation = bean.getClass().getAnnotation(OrderProcessor.class);
            OrderStateEnum[] states = annotation.states();
            OrderEventEnum event = annotation.event();
            String[] bizTypes = annotation.bizTypes();
            String[] sceneIds = annotation.sceneIds();
            init(states, event, bizTypes, sceneIds, (AbstractStateProcessor) bean, stateProcessorMap);
        }
        return bean;
    }

    private <E extends StateProcessor> void init(
            OrderStateEnum[] states, OrderEventEnum event, String[] bizTypes, String[] sceneIds, E processor,
            Map<String, Map<String, Map<String, List<E>>>> stateProcessorMap) {
        for (String bizType : bizTypes) {
            for (String sceneId : sceneIds) {
                Arrays.asList(states).parallelStream().forEach(
                        state -> register(state.name(), event.name(), bizType, sceneId, processor, stateProcessorMap));
            }
        }
    }

    private <E extends StateProcessor> void register(
            String state, String event, String bizType, String sceneId, E processor,
            Map<String, Map<String, Map<String, List<E>>>> stateProcessorMap) {
        if (!stateProcessorMap.containsKey(state)) {
            stateProcessorMap.put(state, new ConcurrentHashMap<>());
        }
        Map<String, Map<String, List<E>>> eventMap = stateProcessorMap.get(state);
        if (!eventMap.containsKey(event)) {
            eventMap.put(event, new ConcurrentHashMap<>());
        }
        Map<String, List<E>> processorMap = eventMap.get(event);
        String bizTypeAndSceneId = buildProcessorKey(bizType, sceneId);
        if (!processorMap.containsKey(bizTypeAndSceneId)) {
            processorMap.put(bizTypeAndSceneId, new CopyOnWriteArrayList<>());
        }
        processorMap.get(bizTypeAndSceneId).add(processor);
    }

    public List<AbstractStateProcessor> getStateProcessors(OrderStateEnum orderState, String eventType, String bizType, String sceneId) {
        Map<String, Map<String, List<AbstractStateProcessor>>> eventMap = stateProcessorMap.get(orderState.name());
        if (CollectionUtils.isEmpty(eventMap)) {
            return Collections.emptyList();
        }
        Map<String, List<AbstractStateProcessor>> processorMap = eventMap.get(eventType);
        if (CollectionUtils.isEmpty(processorMap)) {
            return Collections.emptyList();
        }
        return processorMap.get(buildProcessorKey(bizType, sceneId));
    }

    private String buildProcessorKey(String bizType, String sceneId) {
        return bizType + "@" + sceneId;
    }
}
